/*
 * GammaEliasFede2_gtest.h
 *
 *  Created on: Dec 15, 2012
 *      Author: Federico Orquera
 */

#ifndef GAMMAELIASFEDE2_GTEST_H_
#define GAMMAELIASFEDE2_GTEST_H_

#define max_test_value 65536;

#include <cstring>
#include <memory>
#include <gtest/gtest.h>

#include "../physical_layer/serializers/InvertedListSerializer.h"
#include "../physical_layer/file_system/InvertedList.h"
#include "../physical_layer/compressors/GammaEliasFede/GammaEliasFede.h"

using physical::byte;
using physical::InvertedList;
using physical::InvertedListSerializer;
using physical::InvalidSizeException;
using physical::GammaEliasCoder;

class GammaEliasFedeTestFixture2: public testing::Test {
public:
	std::string m_fileName;

	GammaEliasFedeTestFixture2()
	{
		const ::testing::TestInfo* const test_info =
				::testing::UnitTest::GetInstance()->current_test_info();

		std::string filename(test_info->name());
		filename.append(".");
		filename.append(test_info->test_case_name());
		filename.append(".tmp");

		this->m_fileName = filename;
	}

    void SetUp(){}

    void TearDown(){}

	virtual ~GammaEliasFedeTestFixture2(){}

    const char* getFileNameForTest() {
        return m_fileName.c_str();
    }

    // put in any custom data members that you need
   InvertedListSerializer m_serializer;
};

/**
 * Gamma elias compresion
 */

TEST_F( GammaEliasFedeTestFixture2, GammaEliasCompression) {

	GammaEliasCoder gamma;

	GammaEliasCoder::coding_type testArray[14];
	testArray[0] = 2;	// 010
	testArray[1] = 4;	// 00100
	testArray[2] = 0;	// 10
	testArray[3] = 1;	// 11

	//En el peor de los casos de compresion devuelve algo de este tamaño
	byte dest[ 4*sizeof(GammaEliasCoder::coding_type) + sizeof(GammaEliasCoder::counter_t) ];

	//010 00100 10 11 = (1099)base10
	//010 00100 10 11

	auto size = gamma.compress((char*)&testArray, dest, 4*sizeof(GammaEliasCoder::coding_type));

	EXPECT_EQ(sizeof(GammaEliasCoder::counter_t) + 2*sizeof(byte), size);

	//01000100(68) 00001011(11) <-- completa con ceros adelante de lo q sobra!
	//TODO muy adorable, pero esta mal! Tiene que completar con ceros atras :(
	//01000100 1011000

	EXPECT_EQ((GammaEliasCoder::counter_t)4, ((GammaEliasCoder::counter_t*)dest)[0]);
	EXPECT_EQ((byte)68, (byte)dest[0 + sizeof(GammaEliasCoder::counter_t)]);
	EXPECT_EQ((byte)176, (byte)dest[1 + sizeof(GammaEliasCoder::counter_t)]);

};

/**
 * Gamma Elias con int
 */

TEST_F( GammaEliasFedeTestFixture2, GammaEliasInt) {

	GammaEliasCoder gamma;

	unsigned int testArray[50];

	testArray[0] = 2;	// 010 + 10
	testArray[1] = 4;	// 00100 + 10
	testArray[2] = 0;	// 10 + 10
	testArray[3] = 1;	// 11 + 10
	testArray[4] = 0;	// 10 + 10

	//En el peor de los casos de compresion devuelve algo de este tamaño
	byte dest[ 5*sizeof(unsigned int) + sizeof(GammaEliasCoder::counter_t) ];

	//010 10 00100 10 10 10 11 10 10 10= ()base10

	auto size = gamma.compress( (byte*)&testArray, dest, 5*sizeof(unsigned int) );

	EXPECT_EQ(sizeof(GammaEliasCoder::counter_t) + 3*sizeof(byte), size);

	//01010001(81) 00101010(42) 11101010(234)
	//01010001     00101010     11101010

	EXPECT_EQ((GammaEliasCoder::counter_t)10, (GammaEliasCoder::counter_t)dest[0]);
	EXPECT_EQ((byte)81, (byte)dest[0 + sizeof(GammaEliasCoder::counter_t)]);
	EXPECT_EQ((byte)42, (byte)dest[1 + sizeof(GammaEliasCoder::counter_t)]);
	EXPECT_EQ((byte)234,(byte)dest[2 + sizeof(GammaEliasCoder::counter_t)]);

};

/**
 * Gamma Elias no multiplo de 4
 */

TEST_F( GammaEliasFedeTestFixture2, GammaEliasNotMultipleOfFour) {

	GammaEliasCoder gamma;

	unsigned int testArray[10];

	testArray[0] = 2;	// 010 + 10
	testArray[1] = 4;	// 00100 + 10
	testArray[2] = 0;	// 10 + 10
	testArray[3] = 1;	// 11 + 10
	testArray[4] = 0;	// 10

	//En el peor de los casos de compresion devuelve algo de este tamaño
	byte dest[ 5*sizeof(unsigned int) + sizeof(GammaEliasCoder::counter_t) ];

	//010 10 00100 10 10 10 11 10 10 = ()base10

	auto size = gamma.compress( (byte*)&testArray, dest, 4*sizeof(unsigned int) + sizeof(unsigned short) );

	EXPECT_EQ((size_t)3 + sizeof(GammaEliasCoder::counter_t), size);

	//01010001(81) 00101010(42) 11101000(58)

	EXPECT_EQ((GammaEliasCoder::counter_t)9,  (GammaEliasCoder::counter_t)dest[0]);
	EXPECT_EQ((byte)  81, (byte)  dest[0 + sizeof(GammaEliasCoder::counter_t)]);
	EXPECT_EQ((byte)  42, (byte)  dest[1 + sizeof(GammaEliasCoder::counter_t)]);
	EXPECT_EQ((byte) 232, (byte)  dest[2 + sizeof(GammaEliasCoder::counter_t)]);

};

/**
 * Gamma elias descompresion
 */


TEST_F( GammaEliasFedeTestFixture2, GammaEliasDecompress) {

	GammaEliasCoder gamma;

	byte     compressedBuffer[ 2*sizeof(byte) + sizeof(GammaEliasCoder::counter_t) ];

	((GammaEliasCoder::counter_t*)compressedBuffer)[0] = (GammaEliasCoder::counter_t)4;
	((byte*)  compressedBuffer)[0 + sizeof(GammaEliasCoder::counter_t)] = (byte)68;
	((byte*)  compressedBuffer)[1 + sizeof(GammaEliasCoder::counter_t)] = (byte)176;

	GammaEliasCoder::coding_type dest[14];

	auto size = gamma.uncompress( compressedBuffer, (byte*)&dest, 2*sizeof(byte) + sizeof(GammaEliasCoder::counter_t) );

	EXPECT_EQ((size_t)4*sizeof(GammaEliasCoder::coding_type), size);

	EXPECT_EQ(2, dest[0]);
	EXPECT_EQ(4, dest[1]);
	EXPECT_EQ(0, dest[2]);
	EXPECT_EQ(1, dest[3]);

};

/**
 * Gamma elias descompresion de Int
 */

TEST_F( GammaEliasFedeTestFixture2, GammaEliasUncompressInt) {

	GammaEliasCoder gamma;

	byte     compressedBuffer[ sizeof(GammaEliasCoder::counter_t) + 3*sizeof(byte) ];

	unsigned int testArray[50];

	((GammaEliasCoder::counter_t*)compressedBuffer)[0] = (GammaEliasCoder::counter_t)10;
	((byte*)  compressedBuffer)[0 + sizeof(GammaEliasCoder::counter_t)] = (byte)81;
	((byte*)  compressedBuffer)[1 + sizeof(GammaEliasCoder::counter_t)] = (byte)42;
	((byte*)  compressedBuffer)[2 + sizeof(GammaEliasCoder::counter_t)] = (byte)234;

	auto size = gamma.uncompress( compressedBuffer, (byte*)&testArray,
	        sizeof(GammaEliasCoder::counter_t) + 3*sizeof(byte) );

	EXPECT_EQ((size_t)5*sizeof(unsigned int), size);

	EXPECT_EQ((unsigned int)2, testArray[0]);
	EXPECT_EQ((unsigned int)4, testArray[1]);
	EXPECT_EQ((unsigned int)0, testArray[2]);
	EXPECT_EQ((unsigned int)1, testArray[3]);
	EXPECT_EQ((unsigned int)0, testArray[4]);

};

TEST_F( GammaEliasFedeTestFixture2, PruebaCompressAndUncompressConInvertedListSerializer2 ){

	InvertedList invertedList = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	InvertedList deserializedInvertedList;
	size_t expectedSerializationBytes;
	size_t serializedBytes;
	size_t deserializedBytes;
	byte buffer[128];
	byte compressedBuffer[128];
	byte uncompressedBuffer[128];

	expectedSerializationBytes = m_serializer.getSize( invertedList );

	serializedBytes = m_serializer.serialize( invertedList, buffer );

//----------------------

	std::unique_ptr<physical::Compressor> compressor(new GammaEliasCoder());

	auto tamanioResultanteComprimido = compressor->compress(buffer,compressedBuffer,serializedBytes);

	auto tamanioResultanteDescomprimido = compressor->uncompress(compressedBuffer,uncompressedBuffer,tamanioResultanteComprimido);

	EXPECT_EQ( serializedBytes, tamanioResultanteDescomprimido );

	deserializedBytes = m_serializer.deserialize( uncompressedBuffer, deserializedInvertedList );

//----------------------

	EXPECT_EQ( expectedSerializationBytes, size_t(22) );
	EXPECT_EQ( serializedBytes, size_t(22) );
	EXPECT_EQ( deserializedBytes, size_t(22) );
	EXPECT_EQ( invertedList, deserializedInvertedList );

};

TEST_F( GammaEliasFedeTestFixture2, PruebaTodosLosValoresDeShort ){

	GammaEliasCoder::coding_type
			InputArray [1],
			BufferArray[3],
			OutputArray[1];

	GammaEliasCoder gamma;

	for(unsigned int i = 0; i<65536; i++){
		InputArray[0] = i;
		//std::cout<<"Comprimiendo: "<<InputArray[0]<<std::endl;
		auto compressedSize = gamma.compress((byte*)InputArray, (byte*)BufferArray, sizeof(GammaEliasCoder::coding_type)*1);
		auto uncompressedSize = gamma.uncompress((byte*)BufferArray, (byte*)OutputArray, compressedSize);

		EXPECT_EQ(uncompressedSize,sizeof(GammaEliasCoder::coding_type)*1);
		EXPECT_EQ(OutputArray[0], InputArray[0]);
	}

};

TEST_F( GammaEliasFedeTestFixture2, PruebaTodosLosValoresDeShortAlMismoTiempo ){

    const size_t tamanio_pruebas = 255;

	GammaEliasCoder::coding_type
			InputArray [tamanio_pruebas],
			BufferArray[tamanio_pruebas+sizeof(GammaEliasCoder::counter_t)],
			OutputArray[tamanio_pruebas];

	for(unsigned int i = 0; i<tamanio_pruebas; i++)
		InputArray[i] = i;

	GammaEliasCoder gamma;

	auto compressedSize =   gamma.compress((byte*)InputArray, (byte*)BufferArray,
	        sizeof(GammaEliasCoder::coding_type)*tamanio_pruebas);

	auto uncompressedSize = gamma.uncompress((byte*)BufferArray, (byte*)OutputArray, compressedSize);

	EXPECT_EQ(uncompressedSize,sizeof(GammaEliasCoder::coding_type)*tamanio_pruebas);

	for(unsigned int i = 0; i<tamanio_pruebas; i++)
		EXPECT_EQ(OutputArray[i], InputArray[i]);

};


#endif /* GAMMAELIASFEDE2_GTEST_H_ */
